/*
 * funciones.h
 *
 *  Created on: 15-jul-2009
 *      Author: Pablo Izquierdo & Adrián Romero
 *
 *  Define las funciones necesarias, aqui encontramos las cabeceras de las
 *  funciones y como funcionan.
 */

#ifndef FUNCIONES_H_
#define FUNCIONES_H_

#include "constantes.h"

#include <arpa/inet.h>
#include <limits.h>
#include <linux/if_ether.h>
#include <net/ethernet.h>
#include <net/if.h>
#include <net/route.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/udp.h>
#include <netpacket/packet.h>
#include <pthread.h>
#include <signal.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>

#include <errno.h>

#include "estructuras.h"


void paradaCorrecta();

//baja la interfaz
void bajarInterfaz();

//calcula el checksum de la cabecera ip
unsigned short calcularChecksum(unsigned short *addr, int len);

//devuelve el tamaño del mensaje dhcp
int calcularTamMensajeDhcp();

//cierra el socket de bajo nivel
void cerrarSocketNivelEnlace();

//verifica que los parametros de entrada son correctos y estan en orden
int comprobarParametros(int argc, const char* argv[]);

struct DatosArp* crearNuevoArp();

//inicializa los valores predeterminados del mensaje DHCP
struct DatosDhcp* crearNuevoDhcp();

//Ejecuta el programa
void ejecutar();

int enviarArp();

//Envia un mensaje DHCPDECLINE
int enviarDecline();

//Envia un mensaje DHCPDISCOVER
int enviarDiscover();

int enviarMensajeArp(struct DatosArp *datos, in_addr_t direccionInternet);

//envia un mensaje asociado a un socket de bajo nivel en la capa de subred
int enviarMensajeEthernet(struct DatosDhcp *datos, in_addr_t direccionInternet);

//Envia un mensaje DHCPREQUEST,
int enviarRequest();//si se quiere enviar en broadcast se pondra el parametro a valor 1

//Envia un mensaje DHCPRELEASE
int enviarRelease();

int esArpReply(char* datagrama, int longitud);

int esDhcp( char* ip, int len);

int esUdp( char* ip, int len);
//espera el tiempo indicado en el parametro
void esperar(int microsegundos);

int estaAsignada();

void establecerInfoMensaje(struct DatosDhcp ip_list[]);

//libera toda la memoria del programa para finalizar
void finalizarTodo();

/*
 * Imprime la estructura CadenaDhcp
 * entrada: estructura con los datos en forma de cadena de bytes
 */
void imprimirCadenaDhcp(struct CadenaDhcp *msg);

/*
 * Imprime la estructura DatosDhcp
 * entrada: estructura con los datos en su forma natural
 */
void imprimirDatosDhcp(struct DatosDhcp *datos);

void imprimirDebug(char* method, const char *fmt, ...);

//
void imprimirErrorParametros(int err);

//
void imprimirTraza(int xid, enum mensajeDhcp estado, char* cadena);

//prepara el entorno para ejecutar
int inicializar();

//inicializa las constantes con los valores predeterminados
void inicializarConstantes();

/* Inicializa un mensaje de tipo DHCP, esto es, pone todos los valores a 0 */
void inicializarDatosDhcp(struct DatosDhcp *datos);

//inicializa el socket de bajo nivel
int inicializarSocketNivelEnlace();

//inicializa los temporizadores
void inicializarTemporizadores();

//levanta la interfaz si no lo estaba antes
int levantarInterfaz(const char* interfaz);

/*
 * Libera la memoria ocupada por una estructura CadenaDhcp
 * entrada: estructura con los datos en forma de cadena de bytes
 */
void liberarCadenaDhcp(struct CadenaDhcp *mensaje);

/*
 * Libera la memoria ocupada por una estructura DatosDhcp
 * entrada: estructura con los datos en su forma natural
 */
void liberarDatosDhcp(struct DatosDhcp *datos);

/*
 * Libera toda la memoria de la aplicacion
 */
void liberarMemoria();

//controla la aparicion de la señal de interrupcion del programa
void manejadorSIGINT(int sigint);

//controla la aparicion de la señal de interrupcion del programa
void manejadorSIGUSR2(int sigusr2);

int obtenerArpReply();

int obtenerCabDhcpDesdeMensIp(struct DatosDhcp * dhcp, char * ip_msg, int ip_msg_len);

//
int obtenerCabeceraIp(unsigned char **, in_addr_t, unsigned char *, int);

//
int obtenerCabeceraUdp(unsigned char **, struct CadenaDhcp*);

//obtiene la direccion hardware de nuestra maquina
void obtenerDireccionHW();

//devuelve el indice de la interfaz
int obtenerIndiceInterfaz();
int obtenerLongPaqIp( char* buf, int len);
//
int obtenerOffers(struct DatosDhcp messages[]);

//
char * obtenerMarcaTiempo();

//
int obtenerMensajeAck();

int obtenerMensajeArp(unsigned char** mensaje, struct DatosArp * datos);

//devuelve un mensaje de nivel de subred con los datos de un mensaje dhcp y cabeceras ip y udp
int obtenerMensajeEthernet(unsigned char** mensaje, in_addr_t nombreHost, struct DatosDhcp * datos);

int obtenerOpcionesServidor(struct DatosDhcp ip);

//
void obtenerSiguienteTemporizador(struct timeval *tv);

//devuelve el tamaño del campo opciones para el mensaje DHCPDISCOVER
int obtenerTamOpcionesDiscover(char ** opciones);

//devuelve el tamaño del campo opciones para el mensaje DHCPREQUEST
int obtenerTamOpcionesRequest(char ** opciones);

//devuelve el tamaño del campo opciones para el mensaje DHCPDECLINE
int obtenerTamOpcionesDecline(char ** opciones);

//devuelve el tamaño del campo opciones para el mensaje DHCPRELEASE
int obtenerTamOpcionesRelease(char ** opciones);

//
char* obtenerTrazaConfiguracionRed();

/*
 * Convierte la estructura pasada como cadena de bytes a una estructura de
 * datos
 * entrada: estructura con la cadena de bytes
 * salida: estructura con los datos en su forma natural
 */
int pasarCadenaDhcpADatos(struct DatosDhcp * dhcp, struct CadenaDhcp *message);

struct CadenaDhcp* pasarDatosArpACadena(struct DatosArp *datos);

/*
 * Convierte la estructura pasada como datos a una cadena de bytes
 * entrada: estructura con los datos en su forma natural
 * salida: estructura con la cadena de bytes
 */
struct CadenaDhcp* pasarDatosDhcpACadena(struct DatosDhcp *datos);

//
char* pasarStringAStringHex(char *str, int leng);

void reducirTemporizador(struct timeval *tv, struct timeval *init, struct timeval *end);

void reducirUmbral(struct timeval *umbral);

//transita al estado BOUND
int transitarBound();

//transita al estado INIT
int transitarInit();

//transita al estado REBINDING
int transitarRebinding();

//transita al estado REBOOTING
int transitarRebooting();

//transita al estado RENEWING
int transitarRenewing();

//transita al estado REQUESTING
int transitarRequesting();

//transita al estado SELECTING
int transitarSelecting();

//ejecuta las acciones del estado Bound, 1 si debe transitar a Renewing
int operandoEnBound();

void iniciarTiemposBound();

void obtenerSiguienteTemporizadorT1(struct timeval *tv);


//si se quiere enviar en broadcast se pondra el parametro a valor 1
//int enviarRequestUDP(int broadc);

//si recibe un ACK devuelve un 1, si no devuelve un 0
int operandoEnRenewing();

// Obtiene el mensaje ACK o NACK del servidor
// 1 en caso de ACK, 0 en caso de NACK, -1 en caso de error
int obtenerMensajeAckRenewing();

void inicializarTemporizadores2();

void reducirTemporizadorT1(struct timeval *tv, struct timeval *inicio, struct timeval *fin);

/* Envia un mensaje DHCPRequest por UDP*/
int enviarRequestUDP();

int obtenerCabDhcpDesdeUDP(struct DatosDhcp * dhcp, char * ip_msg, int ip_msg_len);

int enviarMensajeUdpBroadcast(unsigned char* mensaje, uint longitud);
int enviarRequestUDPBroadcast();

int obtenerMensajeAckRebinding();

#endif /* FUNCIONES_H_ */
